package com.dragon.modules.yuegou.service.impl.message;

import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.Notification;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dragon.common.exception.DragonException;
import com.dragon.modules.yuegou.client.wechat_mini.model.Keyword;
import com.dragon.modules.yuegou.client.wechat_mini.request.SubscribeMessageRequest;
import com.dragon.modules.yuegou.client.wechat_mini.service.MiniMultiService;
import com.dragon.modules.yuegou.client.wechat_mini.service.SubscribeMessageService;
import com.dragon.modules.yuegou.entity.message.TemplateEntity;
import com.dragon.modules.yuegou.entity.system.ApplicationEntity;
import com.dragon.modules.yuegou.entity.system.UserEntity;
import com.dragon.modules.yuegou.entity.system.WxOpenidRefEntity;
import com.dragon.modules.yuegou.enums.code.MessageCode;
import com.dragon.modules.yuegou.enums.message.MessageChannel;
import com.dragon.modules.yuegou.enums.message.MessageToType;
import com.dragon.modules.yuegou.enums.message.TemplateStatus;
import com.dragon.modules.yuegou.mapper.message.TemplateMapper;
import com.dragon.modules.yuegou.model.member.Member;
import com.dragon.modules.yuegou.model.message.Message;
import com.dragon.modules.yuegou.model.message.Template;
import com.dragon.modules.yuegou.model.message.TemplateMessage;
import com.dragon.modules.yuegou.model.message.XcxMsgTemplate;
import com.dragon.modules.yuegou.query.message.TemplateQuery;
import com.dragon.modules.yuegou.query.system.WxOpenidRefQuery;
import com.dragon.modules.yuegou.service.member.MemberService;
import com.dragon.modules.yuegou.service.message.MessageService;
import com.dragon.modules.yuegou.service.message.TemplateService;
import com.dragon.modules.yuegou.service.system.ApplicationService;
import com.dragon.modules.yuegou.service.system.UserService;
import com.dragon.modules.yuegou.service.system.WxOpenidRefService;
import com.dragon.modules.yuegou.web.Check;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Nullable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * 消息模板 服务实现
 *
 * @author pbuilder generated
 * @date 2019-03-18 13:46:09
 * @version 2.0
 */
@Slf4j
@Service("msgTemplateService")
@SuppressWarnings("unchecked")
public class TemplateServiceImpl extends ServiceImpl<TemplateMapper, TemplateEntity>
		implements TemplateService {

	@Autowired
	private MessageService messageService;

	@Autowired
	private UserService userService;

	@Autowired
	private MemberService memberService;

	@Autowired
	private com.dragon.modules.yuegou.service.member.WxOpenidRefService memWxOpenidRefService;

	@Autowired
	private WxOpenidRefService sysWxOpenidRefService;

	@Autowired
	private ApplicationService applicationService;

	@Autowired(required = false)
	private JPushClient jpushClient;

	@Autowired(required = false)
	private MiniMultiService miniMultiService;

	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 小程序模板
	 */
	protected static final String XCXMSGTEMPLATE_CACHE_NAME = "ysq:xcxMsgTemplate:";

	/**
	 * 小程序模板
	 */
	protected static final String TEMPLATEKEYWORDS_CACHE_NAME = "ysq:templateKeywords:";

	@Override
	public List<XcxMsgTemplate> getXcxMsgTemplate(String appId) {
		String key = XCXMSGTEMPLATE_CACHE_NAME + appId;
		List<XcxMsgTemplate> list = new ArrayList<>();
		Long size = redisTemplate.opsForList().size(key);
		if (size!=null && size > 0) {
			for (int i = 0; i < size; i++) {
				XcxMsgTemplate xcxMsgTemplate = (XcxMsgTemplate) redisTemplate.opsForList().index(key, i);
				list.add(xcxMsgTemplate);
			}
			return list;
		}

		if (CollectionUtils.isNotEmpty(list)) {
			redisTemplate.delete(key);
			redisTemplate.opsForList().rightPushAll(key, list);
			redisTemplate.expire(key, 1, TimeUnit.DAYS);
		}
		return list;
	}

	public List<Keyword> getKeyWords(String appId, Integer miniTemplateId) {
		String key = TEMPLATEKEYWORDS_CACHE_NAME + ":" + appId + ":" + miniTemplateId;
		List<Keyword> list = new ArrayList<>();
		long size = redisTemplate.opsForList().size(key);
		if (size > 0) {
			for (int i = 0; i < size; i++) {
				list.add((Keyword) redisTemplate.opsForList().index(key, i));
			}
			return list;
		}
		SubscribeMessageService service = miniMultiService.subscribeMessage(appId);
		list = service.getPubTemplateKeyWordsById(miniTemplateId);
		if (CollectionUtils.isNotEmpty(list)) {
			redisTemplate.delete(key);
			redisTemplate.opsForList().rightPushAll(key, list);
			redisTemplate.expire(key, 1, TimeUnit.DAYS);
		}
		return list;
	}

	public String getTemplateId(String appId, Integer tid) {
		List<XcxMsgTemplate> templateList = getXcxMsgTemplate(appId);
		for (XcxMsgTemplate xcxMsgTemplate : templateList) {
			if (xcxMsgTemplate.getTemplateNum().equals(tid)) {
				return xcxMsgTemplate.getTemplateId();
			}
		}
		log.debug("找不到消息模板 appId={}，tid={}",appId,tid);
		throw new DragonException(MessageCode.TEMPLATE_NOT_FOUND);
	}

	@Override
	public void saveTemplate(Template template) {
		Check.notNull(template.getMsgType(), "模板类型不能为空");
		Check.notEmpty(template.getName(), "模板名称不能为空");
		Check.notEmpty(template.getContent(), "模板内容不能为空");
		Check.notNull(template.getArgNum(), "参数数量不能为空");
		TemplateQuery templateQuery = new TemplateQuery();
		templateQuery.setName(template.getName());
		if (StringUtils.isEmpty(template.getId())) {
			long count = count(templateQuery.toQueryWrapper());
			if (count > 0) {
				throw new DragonException(MessageCode.TEMPLATE_TITLE_EXISTS);
			}
			// 新增模板
			template.setStatus(TemplateStatus.ENABLED);
			template.setDel(false);
			save(template);
		} else {
			List<TemplateEntity> list = list(templateQuery.toQueryWrapper());
			if (CollectionUtils.isNotEmpty(list) && !list.get(0).getId().equals(template.getId())) {
				throw new DragonException(MessageCode.TEMPLATE_TITLE_EXISTS);
			}
			// 修改模板
			updateById(template);
		}
	}

	@Override
	public void sendTemplateMessage(TemplateMessage templateMessage) {
		Template template = new Template(getById(templateMessage.getTemplateId()));
		String content = template.render(templateMessage.getArgValues());
		templateMessage.getChannels().forEach(channel -> sendByChannel(channel, templateMessage, template, content));
	}

	private void sendByChannel(MessageChannel channel, TemplateMessage templateMessage, Template template,
							   String content) {
		switch (channel) {
		case SYSTEM:
			sendSystemMessage(templateMessage, content);
			break;
		case SMS:
			sendSmsMessage(templateMessage, template);
			break;
		case EMAIL:
			break;
		case JPUSH:
			sendJpushMessage(templateMessage, content);
			break;
		case WECHAT:
			// 暂时微信通道推送微信小程序订阅消息
			sendMiniMessage(templateMessage, template, content);
		}
	}

	/**
	 * 发送系统消息
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @param content
	 *            渲染后的内容
	 */
	private void sendSystemMessage(TemplateMessage templateMessage, String content) {
		Message message = templateMessage.createMessage(MessageChannel.SYSTEM);
		message.setReached(false);
		message.setContent(content);
		messageService.save(message);
		logMessage(message);
	}

	/**
	 * 发送短信
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @param template
	 *            模板
	 */
	private void sendSmsMessage(TemplateMessage templateMessage, Template template) {
		Message message = templateMessage.createMessage(MessageChannel.SMS);
		message.setCreatedTime(LocalDateTime.now());
		boolean reached = sendByThirdSmsChannel(templateMessage);
		message.setReachedTime(LocalDateTime.now());
		message.setReached(reached);
		message.setContent(template.render(templateMessage.getArgValues()));
		messageService.save(message);
		logMessage(message);
	}

	/**
	 * 极光推送
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @param content
	 *            渲染后的内容
	 */
	private void sendJpushMessage(TemplateMessage templateMessage, String content) {
		Message message = templateMessage.createMessage(MessageChannel.JPUSH);
		message.setCreatedTime(LocalDateTime.now());
		boolean reached = sendByThirdJPushChannel(templateMessage, content);
		message.setReachedTime(LocalDateTime.now());
		message.setReached(reached);
		message.setContent(content);
		messageService.save(message);
		logMessage(message);
	}

	/**
	 * 发送微信小程序订阅消息
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @param template
	 *            模板
	 * @param content
	 *            渲染后的内容
	 */
	private void sendMiniMessage(TemplateMessage templateMessage, Template template, String content) {
		Message message = templateMessage.createMessage(MessageChannel.WECHAT);
		message.setCreatedTime(LocalDateTime.now());
		boolean reached = sendByThirdMiniChannel(templateMessage, template);
		message.setReachedTime(LocalDateTime.now());
		message.setReached(reached);
		message.setContent(content);
		messageService.save(message);
		logMessage(message);
	}

	/**
	 * 记录日志
	 * 
	 * @param message
	 *            消息
	 */
	private void logMessage(Message message) {
		log.debug("[推送消息][类型={}][通道={}][目标={}#{}][{}]", message.getMsgType().getAlias(),
				message.getChannel().getAlias(), message.getToType().getAlias(), message.getToUser(),
				message.getContent());
	}

	/**
	 * 通过第三方短信通道推送短信
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @return 推送结果
	 */
	private Boolean sendByThirdSmsChannel(TemplateMessage templateMessage) {
		try {
			String mobile = getMobile(templateMessage.getToType(), templateMessage.getToUser());
			if (StringUtils.isEmpty(mobile)) {
				log.debug("未推送短信：因为用户没有手机号");
				return false;
			}
			log.debug("未推送短信");
			return true;
		} catch (RuntimeException e) {
			log.error("发送短信失败", e);
			return false;
		}
	}

	/**
	 * 通过第三方极光推送通道推送APP消息
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @param content
	 *            模板
	 * @return 推送结果
	 */
	private Boolean sendByThirdJPushChannel(TemplateMessage templateMessage, String content) {
		try {
			String regId = getJPushRegistrationId(templateMessage.getToType(), templateMessage.getToUser());
			if (StringUtils.isNotEmpty(regId)&& jpushClient!=null) {
				PushResult result = jpushClient.sendPush(PushPayload.newBuilder().setPlatform(Platform.all())
						.setAudience(Audience.registrationId(regId)).setNotification(Notification.alert(content)).build());
				return result.isResultOK();
			}
			log.debug("未推送消息：因为没有对接极光推送平台");

		} catch (Exception e) {
			log.error("发送消息失败", e);
		}
		return false;
	}

	/**
	 * 通过第三方微信小程序通道推送消息
	 * 
	 * @param templateMessage
	 *            模板消息
	 * @param template
	 *            模板
	 * @return 推送结果
	 */
	private boolean sendByThirdMiniChannel(TemplateMessage templateMessage, Template template) {
		if (miniMultiService == null) {
			log.debug("未推送消息：因为没有对接微信小程序");
			return false;
		}
		try {
			String miniTemplateId = template.getWechatTemplateId();
			if (StringUtils.isEmpty(miniTemplateId)) {
				log.debug("未推送消息：模板没有关联微信小程序号消息模板");
				return false;
			}
			ApplicationEntity app = applicationService.getById(templateMessage.getAppid());
			if (app == null) {
				log.debug("未推送消息：消息来源于未知应用");
				return false;
			}
			String openid = getWechatMiniOpenid(app, templateMessage.getToType(), templateMessage.getToUser());
			if (StringUtils.isEmpty(openid)) {
				log.debug("未推送消息：用户没有所在小程序的openid");
				return false;
			}
			SubscribeMessageRequest request = new SubscribeMessageRequest();
			request.setTemplateId(getTemplateId(app.getId(), Integer.parseInt(miniTemplateId)));
			request.setPage(templateMessage.getUrl());
			request.setToUser(openid);
			SubscribeMessageService service = miniMultiService.subscribeMessage(templateMessage.getAppid());
			List<Keyword> keywords = getKeyWords(templateMessage.getAppid(), Integer.parseInt(miniTemplateId));
			Map<String, String> keywordMap = new HashMap<>();
			if (CollectionUtils.isNotEmpty(keywords)) {
				for (Keyword keyword : keywords) {
					keywordMap.put(keyword.getName(), keyword.getRule() + keyword.getKid());
				}
			}
			for (int i = 0, size = Math.min(templateMessage.getArgNames().size(),
					templateMessage.getArgValues().size()); i < size; i++) {
				request.put(keywordMap.get(templateMessage.getArgNames().get(i)),
						templateMessage.getArgValues().get(i));
			}
			log.debug("发送消息请求={}", JSON.toJSONString(request));
			return service.sendRawMessage(request);
		} catch (Exception e) {
			log.error("发送消息失败", e);
			return false;
		}
	}

	/**
	 * 获取用户的手机号
	 * 
	 * @param toType
	 *            目标类型
	 * @param toUser
	 *            目标用户ID
	 * @return 手机号
	 */
	private String getMobile(MessageToType toType, String toUser) {
		switch (toType) {
		/**
		 * 会员
		 */
		case MEMBER:
			Member member = memberService.getMember(toUser);
			return member != null ? member.getMobile() : null;
		/**
		 * 用户
		 */
		case USER:
			UserEntity user = userService.getById(toUser);
			return user != null ? user.getMobile() : null;
		/**
		 * 系统
		 */
		case SYSTEM:
			// 没有系统手机号联系方式
			return null;
		}
		return null;
	}

	/**
	 * 获取用户的极光推送注册ID
	 * 
	 * @param toType
	 *            目标类型
	 * @param toUser
	 *            目标用户ID
	 * @return openid
	 */
	private String getJPushRegistrationId(MessageToType toType, String toUser) {
		switch (toType) {
		/**
		 * 会员
		 */
		case MEMBER:
			Member member = memberService.getMember(toUser);
			return member != null ? member.getJpushRegId() : null;
		/**
		 * 用户
		 */
		case USER:
			return null;
		/**
		 * 系统
		 */
		case SYSTEM:
			return null;
		}
		return null;
	}

	/**
	 * 获取用户的微信openid
	 * 
	 * @param toType
	 *            目标类型
	 * @param toUser
	 *            目标用户ID
	 * @return openid
	 */
	private String getWechatMiniOpenid(ApplicationEntity app, MessageToType toType, String toUser) {
		if (MessageToType.MEMBER.equals(toType)) {
			return findMemberMiniOpenid(app, toUser);
		} else {
			return findUserMiniOpenid(app, toUser);
		}
	}


	/**
	 * 查找会员的微信小程序openid
	 * 
	 * @param app
	 *            应用
	 * @param memberId
	 *            会员ID
	 * @return openid
	 */
	@Nullable
	private String findMemberMiniOpenid(ApplicationEntity app, String memberId) {
		Check.notNull(memberId, "memberId不能为null");
		if (StringUtils.isEmpty(app.getAppid())) {
			return null;
		}
		com.dragon.modules.yuegou.query.member.WxOpenidRefQuery query = new com.dragon.modules.yuegou.query.member.WxOpenidRefQuery();
		query.memberId().eq(memberId);
		query.appid().eq(app.getAppid());
		List<com.dragon.modules.yuegou.entity.member.WxOpenidRefEntity> ref = memWxOpenidRefService.list(query.toQueryWrapper());
		return CollectionUtils.isNotEmpty(ref) ? ref.get(0).getWxOpenId() : null;
	}

	/**
	 * 查找用户的微信小程序openid
	 * 
	 * @param app
	 *            应用
	 * @param userId
	 *            会员ID
	 * @return openid
	 */
	@Nullable
	private String findUserMiniOpenid(ApplicationEntity app, String userId) {
		Check.notNull(userId, "userId不能为null");
		if (StringUtils.isEmpty(app.getAppid())) {
			return null;
		}
		WxOpenidRefQuery query = new WxOpenidRefQuery();
		query.userId().eq(userId);
		query.appid().eq(app.getAppid());
		List<WxOpenidRefEntity> ref = sysWxOpenidRefService.list(query.toQueryWrapper());
		return CollectionUtils.isNotEmpty(ref) ? ref.get(0).getWxOpenId() : null;
	}
}